package copyengine.utils
{

    import flash.display.DisplayObject;
    import flash.display.DisplayObjectContainer;
    import flash.display.MovieClip;
    import flash.events.EventDispatcher;
    import flash.external.ExternalInterface;
    import flash.utils.Dictionary;

    import org.as3commons.reflect.Accessor;
    import org.as3commons.reflect.Field;
    import org.as3commons.reflect.Type;
    import org.as3commons.reflect.Variable;

    public final class GeneralUtils
    {
        public function GeneralUtils()
        {
        }


        [Inline]
        public static function addTargetEventListener(_target:EventDispatcher, _type:String, _listener:Function, _useCapture:Boolean = false, _priority:int = 0, _useWeakReference:Boolean = true):void
        {
            _target && _target.addEventListener(_type, _listener, _useCapture, _priority, _useWeakReference);
        }

        [Inline]
        public static function removeTargetEventListener(_target:EventDispatcher, _type:String, _listener:Function, _useCapture:Boolean = false):void
        {
            _target && _target.removeEventListener(_type, _listener, _useCapture);
        }

        public static const ADD_ORDER_TYPE_TOP:int = 1;
        public static const ADD_ORDER_TYPE_LAST:int = 2;

        public static function addTargetToParent(_target:DisplayObject, _parent:DisplayObjectContainer, _adjustOrder:int = ADD_ORDER_TYPE_TOP):void
        {
            if (_target != null && _parent != null) {
                switch (_adjustOrder) {
                    case ADD_ORDER_TYPE_LAST:
                    {
                        _parent.addChildAt(_target, 0);
                        break;
                    }
                    case ADD_ORDER_TYPE_TOP:
                    {
                        _parent.addChild(_target);
                        break;
                    }
                }
            }
        }


        [Inline]
        public static function removeTargetFromParent(_target:DisplayObject):void
        {
            if (_target != null) {
                if (_target is MovieClip) {
                    (_target as MovieClip).stop();
                }
                if (_target.parent != null) {
                    _target.parent.removeChild(_target);
                }
            }
        }

        /**
         * 调用 一个Array或者Vector中所有实现了ICEDispose接口的Dispose函数
         * 并且把当前Array或者Vector length设置为0
         */
        [Inline]
        public static function disposeAllDisposeAbleObjectInArrayOrVector(_array:*):void
        {
            if (_array != null) {
                for each(var element:* in _array) {
                    if (element is ICEDispose) {
                        (element as ICEDispose).dispose();
                    }
                }
                _array.length = 0;
            }
        }

        /**
         * Add one target to an array ,
         * if the array already have the target then return false , else return true
         */
        [Inline]
        public static function addElementToArrayOrVector(_element:*, _array:*):Boolean
        {
            if (_array.indexOf(_element) == -1) {
                _array.push(_element);
                return true;
            }
            else {
                return false;
            }
        }

        [Inline]
        public static function removeElementFromArrayOrVector(_element:*, _array:*):Boolean
        {
            if (_array) {
                var id:int = _array.indexOf(_element);
                if (id >= 0) {
                    _array.splice(id, 1);
                    return true;
                }
            }
            return false;
        }

        public static function swapObjectInArray(_objOneIndex:int, _objTwoIndex:int, _array:Array):void
        {
            if (_objOneIndex < _array.length && _objTwoIndex < _array.length) {
                var obj:Object = _array[_objOneIndex];
                _array[_objOneIndex] = _array[_objTwoIndex];
                _array[_objTwoIndex] = obj;
                obj = null;
            }
        }

        /**
         * return a<= x <= y
         */
        public static function isBetweenValue(_number:int, _downLine:int, _upLine:int):Boolean
        {
            if (_number >= _downLine && _number <= _upLine) {
                return true;
            }
            else {
                return false;
            }
        }

        /**
         * return value : _low<=_val<=_high
         */
        [Inline]
        public static function normalizingValue(_val:Number, _low:Number, _high:Number):Number
        {
            //防止low value 和 hight value 越界范围变成反向
            if (_low > _high) {
                var tempValue:int = _low;
                _low = _high;
                _high = tempValue;
            }
            _val = Math.max(_low, _val);
            return Math.min(_val, _high);
        }

        static public function compareNumber(_n1:Number, _n2:Number, _tolerant:Number = 0.1):Boolean
        {
            var diff:Number = Math.abs(_n1 - _n2);
            if (diff <= _tolerant) {
                return true;
            }
            return false;

        }

        // compare only date, not include time
        // return true: equal
        static public function compareDate(date1:Date, date2:Date):Boolean
        {
            var result:Boolean = false;
            if (date1 == null) {
                if (date2 == null) {
                    return true;
                }
                return result;
            }

            if (date2 == null) {
                return result;
            }

            result = compareNumber(date1.getFullYear(), date2.getFullYear());
            if (result) {
                result = compareNumber(date1.getMonth(), date2.getMonth());
            }
            if (result) {
                result = compareNumber(date1.getDate(), date2.getDate());
            }
            return result;
        }

        static public function getCookie(cookieName:String):String
        {
            var r:String = "";
            var search:String = cookieName + "=";
            var js:String = "function get_cookie(){return document.cookie;}";
            var o:Object = ExternalInterface.call(js);
            var cookieVariable:String = o.toString();

            if (cookieVariable.length > 0) {
                var offset:int = cookieVariable.indexOf(search);
                if (offset != -1) {
                    offset += search.length;
                    var end:int = cookieVariable.indexOf(";", offset);
                    if (end == -1) {
                        end = cookieVariable.length;
                    }
                    r = unescape(cookieVariable.substring(offset, end));
                }
            }
            return r;
        }

        static public function setCookie(cookieName:String, cookieValue:String):void
        {
            var js:String = "function sc(){";
            js += "var c = escape('" + cookieName + "') + '=' + escape('" + cookieValue + "') + '; path=/';";
            js += "document.cookie = c;";
            js += "}";
            ExternalInterface.call(js);
        }

        //====================================//
        //========== Clone & 序列化与反序列化 ========//
        //====================================//
        public static function cloneObject(_obj:*):*
        {
            return doSerializeObject(_obj, SERIALIZE_TYPE_CLONE);
        }

        public static function serializeObject(_obj:*):Object
        {
            return doSerializeObject(_obj, SERIALIZE_TYPE_TRANSFER);
        }

        public static function convertToJsonObject(_obj:*):Object
        {
            return doSerializeObject(_obj, SERIALIZE_TYPE_FOR_JSON);
        }

        public static function deserializeObject(_obj:Object):*
        {
            return doDeserializeObject(_obj);
        }


        //========================================================//
        //===                                         反序列化                                                            ====//
        //========================================================//

        private static function doDeserializeObject(_transportLayerData:Object):*
        {
            var returnObjType:Type = Type.forName(_transportLayerData["classFullName"]);
            var warpObject:* = _transportLayerData["warpObject"];

            var returnObj:* = new returnObjType.clazz();
            if (returnObj is Vector.<*> || returnObj is Array) {
                for each (var subObject:* in warpObject) {
                    if (subObject is String || subObject is int || subObject is uint || subObject is Number || subObject is Boolean) {
                        returnObj.push(subObject);
                    }
                    else {
                        returnObj.push(doDeserializeObject(subObject));
                    }
                }
            }
            else {
                var cloneProperty:* = null;
                //如果classFullName为Object 则表示为动态Object
                if (_transportLayerData["classFullName"] == "Object") {
                    for (var key:String in warpObject) {
                        cloneProperty = doDeserializeProperty(warpObject[key], null);
                        returnObj[key] = cloneProperty;
                    }
                }
                else {
                    for each (var property:Field in returnObjType.properties) {
                        if (property is Variable || property is Accessor && Accessor(property).writeable && property.name != 'prototype') {
                            cloneProperty = doDeserializeProperty(warpObject[property.name], property);
                            returnObj[property.name] = cloneProperty;
                        }
                    }
                }
            }
            return returnObj;
        }

        private static function doDeserializeProperty(_sourceProperty:*, _currentField:Field):*
        {
            if (_sourceProperty != null) {
                if (_sourceProperty is Boolean || _sourceProperty is String || _sourceProperty is int || _sourceProperty is uint || _sourceProperty is Number) {
                    return _sourceProperty;
                }
                else {
                    return doDeserializeObject(_sourceProperty);
                }
            }
            else {
                return null;
            }
        }

        //========================================================//
        //===                                         序列化 or Clone                                                 ====//
        //========================================================//

        /**
         *序列化类型：Clone  标示该类型则会直接深度Clone当前对象
         */
        private static const SERIALIZE_TYPE_CLONE:int = 1;

        /**
         * 序列化类型：Transfer 标示该类型则会将当前对象转发为可以使用反序列化函数不丢失类型的转化对象
         * 注意::只有反序列化函数才可以解析该类型对象
         */
        private static const SERIALIZE_TYPE_TRANSFER:int = 2;

        /**
         * 序列化类型：JSON 标示该类型则会将对象转发为可以被JSON对象识别的可序列化对象
         */
        private static const SERIALIZE_TYPE_FOR_JSON:int = 3;

        /**
         *序列化 or Clone对象
         *
         * 序列化后的对象及非原来对象，需要调用反序列化才可恢复 而Clone后的对象及为原始对象的Clone
         *
         */
        private static function doSerializeObject(_normalObj:*, _serializeType:int):Object
        {
            //注意!!::
            //原来ObjectType是部分由递归函数传入,为了简化取Type的次数,不过由于当给Type指定为"*"时候
            //logic无法取得当前_normalObj的真实Type,所以所有的Tpye都重新从_normalObj取一次
            var objType:Type = Type.forInstance(_normalObj);
            var returnCloneObj:Object;

            if (_normalObj != null) {
                if (_normalObj is Vector.<*> || _normalObj is Array) {
                    returnCloneObj = doSerializeArrayAndVectorObj(_normalObj, _serializeType, objType);
                }
                else if (_normalObj is Dictionary) {
                    returnCloneObj = doSerializeDictionaryObj(_normalObj, _serializeType, objType);
                }
                else {
                    returnCloneObj = doSerializeNotListStructureObj(_normalObj, _serializeType, objType);
                }

                if (_serializeType == SERIALIZE_TYPE_TRANSFER) {
                    var transportWarp:Object = {}
                    transportWarp["isCETransportObject"] = true;
                    transportWarp["classFullName"] = objType.fullName;
                    transportWarp["warpObject"] = returnCloneObj;
                    return transportWarp;
                }
                else {
                    return returnCloneObj;
                }
            }
            return null;

        }

        private static function doSerializeDictionaryObj(_normalObj:*, _serializeType:int, _objType:Type):Object
        {
            var returnCloneObj:Object;
            //如果为Clone对象则直接使用原始对象的类型
            if (_serializeType == SERIALIZE_TYPE_CLONE) {
                returnCloneObj = new _objType.clazz();
            }
            else if (_serializeType == SERIALIZE_TYPE_FOR_JSON) {
                returnCloneObj = {};
            }
            //如果是序列化对象则直接存入Array(可以混存,防止原对象为混存对象)
            else {
                throw new Error("Can't serialize dictionary obj(but can clone) right now, as there is not space to store the key");
            }
            for (var key:Object in _normalObj) {
                var subObject:* = _normalObj[key];
                if (subObject is String || subObject is int || subObject is uint || subObject is Number || subObject is Boolean) {
                    //基础类型 直接Push进去
                    returnCloneObj[key] = subObject;
                }
                else {
                    returnCloneObj[key] = doSerializeObject(subObject, _serializeType);
                }
            }
            return returnCloneObj;
        }

        /**
         * 序列化Array or Vector结构的Object
         */
        private static function doSerializeArrayAndVectorObj(_normalObj:*, _serializeType:int, _objType:Type):Object
        {
            var returnCloneObj:Object;
            //如果为Clone对象则直接使用原始对象的类型
            if (_serializeType == SERIALIZE_TYPE_CLONE) {
                returnCloneObj = new _objType.clazz();
            }
            //如果是序列化对象则直接存入Array(可以混存,防止原对象为混存对象)
            else {
                returnCloneObj = [];
            }
            for each (var subObject:* in _normalObj) {
                if (subObject is String || subObject is int || subObject is uint || subObject is Number || subObject is Boolean) {
                    //基础类型 直接Push进去
                    returnCloneObj.push(subObject);
                }
                else {
                    returnCloneObj.push(doSerializeObject(subObject, _serializeType));
                }
            }
            return returnCloneObj;
        }

        /**
         * 序列化不为List结构(Array,Vector,Dictonary)的Object
         */
        private static function doSerializeNotListStructureObj(_normalObj:*, _serializeType:int, _objType:Type):Object
        {
            var returnCloneObj:Object;
            if (_serializeType == SERIALIZE_TYPE_CLONE) {
                returnCloneObj = new _objType.clazz();
            }
            else {
                returnCloneObj = {}
            }
            var cloneProperty:* = null;
            //动态的情况直接for循环取出Key值
            //非动态的情况下无法for循环取Key值
            if (_objType.fullName == "Object") {
                for (var key:String in _normalObj) {
                    if (_normalObj[key] != null) {
                        cloneProperty = doSerializeProperty(_normalObj[key], _serializeType, null);
                        returnCloneObj[key] = cloneProperty;
                    }
                }
            }
            else {
                for each (var property:Field in _objType.properties) {
                    if (property is Variable || property is Accessor && Accessor(property).writeable && property.name != 'prototype') {
                        if (_normalObj[property.name] != null) {
                            cloneProperty = doSerializeProperty(_normalObj[property.name], _serializeType, property);
                            returnCloneObj[property.name] = cloneProperty;
                        }
                    }
                }
            }
            return returnCloneObj;
        }

        private static function doSerializeProperty(_sourceProperty:*, _serializeType:int, _currentField:Field):*
        {
            //如果有Field则是非动态Object,否则为动态Object
            if (_currentField) {
                if (_currentField is Variable || _currentField is Accessor && Accessor(_currentField).writeable && _currentField.name != 'prototype') {
                    if (_currentField.type.fullName == "Boolean" || _currentField.type.fullName == "String" || _currentField.type.fullName == "int" || _currentField.type.fullName == "uint" || _currentField.
                                    type.fullName == "Number") {
                        //基本类型
                        return _sourceProperty;
                    }
                    else {
                        return doSerializeObject(_sourceProperty, _serializeType);
                    }
                }
            }
            else {
                if (_sourceProperty is Boolean || _sourceProperty is String || _sourceProperty is int || _sourceProperty is uint || _sourceProperty is Number) {
                    return _sourceProperty;
                }
                else {
                    return doSerializeObject(_sourceProperty, _serializeType);
                }
            }
        }

    }
}


